package cn.buk.tms.bill.service;

import cn.buk.api.pay.alipay.dto.BillAccountLogRequest;
import cn.buk.common.JsonResult;
import cn.buk.common.util.DateUtil;
import cn.buk.tms.dao.BillDao;
import cn.buk.tms.dao.SettlementDao;
import cn.buk.tms.dto.AuthInfo;
import cn.buk.tms.entity.bill.ReceiptHistory;
import cn.buk.tms.entity.bill.ReceiptInfo;
import cn.buk.tms.entity.bill.SettlementSheet;
import cn.buk.tms.entity.order.OrderNoInfo;
import cn.buk.tms.sc.SearchReceiptCriteria;
import cn.buk.qms.service.BaseAbstractService;
import cn.buk.tms.entity.bill.BillInfo;
import cn.buk.tms.entity.misc.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Service;

import jakarta.transaction.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static cn.buk.common.JsonResult.STATUS_OK;
import static cn.buk.tms.entity.bill.ReceiptInfo.RECEIPT_TYPE_RECEIVE;
import static cn.buk.tms.entity.bill.ReceiptInfo.RECEIPT_TYPE_REFUND;
import static cn.buk.qms.service.BillServiceImpl.*;
import static cn.buk.tms.common.constant.TmsOrderConstant.BILL_NOT_AUDITED;
import static cn.buk.tms.common.constant.TmsOrderConstant.MANUAL_WRITE_OFF_REMARK;

@Service
public class ReceiptServiceImpl extends BaseAbstractService implements ReceiptService {

  private static final Logger logger = LogManager.getLogger(ReceiptServiceImpl.class);

  @Autowired
  private BillDao billDao;


  @Autowired
  private SettlementDao settlementDao;

  /**
   * 创建收款单 1、创建收款单 2、在企业客户的现金账户的金额上加上本次的金额 3、发送微信通知
   *
   * @param authInfo   当前用户信息
   * @param customerId 客户id
   * @param amount     收款金额
   * @param remark     备注
   */
  @Override
  @Secured({"ROLE_ADMIN", "ROLE_CASHIER"})
  public JsonResult createReceipt(AuthInfo authInfo, final int customerId,
                                  final double amount, final String remark, String receiptBank, Date receiptDate,
                                  Integer[] receiptIds) {
    if (StringUtils.isBlank(remark)) {
      return JsonResult.createJsonResult(-2, "备注不能为空");
    }

    final int enterpriseId = authInfo.getEnterpriseId();
    final String username = authInfo.getUsername();

    Customer customer = null;
    int customerType;
    int customerEnterpriseId = 0;

    if (customerId == 0) {
      //散客
      customerType = 0;
    } else {
      //企业客户
      customerType = 1;
      customer = customerDao.getCustomerById(customerId);
      if (customer == null || customer.getEnterpriseId() != enterpriseId) {
        return JsonResult.createJsonResult(-1, ERROR_MESSAGE_CUSTOMER_NOT_EXISTED);
      }

      customerEnterpriseId = customer.getCustomerEnterpriseId();
    }

    final String receiptNo = orderNoGenerator.generateOrderNo(enterpriseId, OrderNoInfo.ENT_RECEIPT);

    ReceiptInfo info = new ReceiptInfo();
    info.setEnterpriseId(enterpriseId);
    info.setCustomerType(customerType);
    info.setCustomer(customer);

    info.setOperator(username);

    info.setReceiptNo(receiptNo);
    info.setAmount(amount);

    if (amount >= 0) {
      info.setReceiptType(RECEIPT_TYPE_RECEIVE);
    } else if (amount < 0) {
      // 退款单
      info.setReceiptType(RECEIPT_TYPE_REFUND);
    }

    info.setRemark(remark);

    info.setReceiptBank(receiptBank);
    info.setReceiptDate(receiptDate);

    int retCode = billDao.createReceiptInfo(enterpriseId, customerEnterpriseId, info, receiptIds);

    return JsonResult.createJsonResult(retCode);
  }

  /**
   * 查找收款单
   */
  @Override
  @Secured({RoleInfo.ROLE_SUPERVISOR, RoleInfo.ROLE_CASHIER, RoleInfo.ROLE_ADMIN})
  public List<ReceiptInfo> searchReceipts(int enterpriseId, int userId, SearchReceiptCriteria sc) {
    if (isOnlySupervisor(userId)) {
      List<Integer> allowedCustomerIds = searchAllowedCustomerIds(enterpriseId, userId);
      if (allowedCustomerIds.isEmpty()) {
        return new ArrayList<>();
      } else {
        return billDao.searchReceipts(enterpriseId, sc, allowedCustomerIds);
      }
    } else {
      return billDao.searchReceipts(enterpriseId, sc, null);
    }
  }


  @Override
  public ReceiptInfo refreshReceiptInfo(int enterpriseId, int id) {
    ReceiptInfo info = billDao.refreshReceiptInfo(id);
    if (info != null && info.getEnterpriseId() != enterpriseId) {
      info = null;
    }

    return info;
  }


  @Override
  @Secured({RoleInfo.ROLE_CASHIER, RoleInfo.ROLE_ADMIN})
  public JsonResult writeOffBillInfo(final int enterpriseId, String operator, final int receiptId,
                                     final Integer billId, final String billNo, final boolean ignoreNegativeNum) {
    ReceiptInfo receiptInfo = this.refreshReceiptInfo(enterpriseId, receiptId);
    var jr = validateReceiptInfoForWriteOff(receiptInfo);
    if (jr != null) {
      return jr;
    }

    BillInfo billInfo = billDao.getBillInfoByIdOrNo(enterpriseId, billId, billNo);
    jr = validateBillInfo4WriteOff(enterpriseId, billInfo);
    if (jr != null) {
      return jr;
    }

    jr = validateBillAndReceipt(billInfo, receiptInfo, ignoreNegativeNum);
    if (jr != null) {
      return jr;
    }

    int retCode = billDao.writeOffBill(receiptId, billInfo.getId(), operator, ignoreNegativeNum);

    return JsonResult.createJsonResult(retCode);
  }


  @Override
  @Transactional
  @Secured({RoleInfo.ROLE_CASHIER, RoleInfo.ROLE_ADMIN})
  public JsonResult writeOffBillsByReceipt(AuthInfo authInfo, int customerId, Integer[] billIds,
                                           int receiptId, double amount, String remark, String receiptBank, Date receiptDate) {
    if (billIds == null || billIds.length == 0) {
      return JsonResult.createJsonResult(-1, "请选择账单号");
    }

    if (receiptId <= 0 && StringUtils.isBlank(remark)) {
      return JsonResult.createJsonResult(-2, "新建收款单需要备注");
    }

    if (receiptId <= 0) {
      //创建新收款单，并将新收款单的id复制给receiptId
      JsonResult jr = this.createReceipt(authInfo, customerId, amount, remark, receiptBank, receiptDate, null);
      if (STATUS_OK.equalsIgnoreCase(jr.getStatus())) {
        receiptId = jr.getReturnCode();
      } else {
        return jr;
      }
    }

    double totalBillSum = 0;
    for (int billId : billIds) {
      BillInfo billInfo = billDao.getBillInfo(billId, false);
      if (billInfo == null) {
        return JsonResult.createJsonResult(-3, "账单(" + billId + ")不存在");
      }

      totalBillSum += billInfo.getBillSum();
    }

    final int enterpriseId = authInfo.getEnterpriseId();
    final String username = authInfo.getUsername();
    for (int billId : billIds) {
      JsonResult jr = this.writeOffBillInfo(enterpriseId, username, receiptId, billId, null, totalBillSum > 0);
      if (!STATUS_OK.equalsIgnoreCase(jr.getStatus())) {
        throw new RuntimeException(jr.getErrmsg());
      }
    }

    //debug
    doSendAlert(enterpriseId, "用收款单(" + receiptId + ")直接对多张账单销账", "Dai", 0);

    return JsonResult.createJsonResult(1);
  }


  /**
   * 撤销账单销账
   */
  @Override
  @Secured({RoleInfo.ROLE_ADMIN})
  public JsonResult undoWriteOffBill(final int enterpriseId, final String username,
                                     final int billId, final String remark) {
    if (!"OK".equalsIgnoreCase(remark)) {
      return JsonResult.createJsonResult(-1, "请输入 OK，以确认撤销销账。");
    }

    BillInfo billInfo = billDao.getBillInfo(billId);

    if (billInfo == null || billInfo.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-3, ERROR_MESSAGE_BILL_NOT_EXISTED);
    }

    if (BILL_NOT_AUDITED.equalsIgnoreCase(billInfo.getBePaid())) {
      return JsonResult.createJsonResult(-4, "此账单未核销");
    }

    if (!billInfo.getSettlementNo().contains(MANUAL_WRITE_OFF_REMARK)) {
      return JsonResult.createJsonResult(-5, "此账单不是" + MANUAL_WRITE_OFF_REMARK);
    }

    int retCode = billDao.undoWriteOffBill(enterpriseId, username, billId);

    if (retCode > 0) {
      String remark1 = "撤销账单(" + billInfo.getBillNum() + ")销账:成功。";
      doSendAlert(enterpriseId, remark1, null, EntDepartment.DEPT_ADMINISTRATOR);
    }

    return JsonResult.createJsonResult(retCode);
  }


  @Override
  @Secured({"ROLE_CASHIER", "ROLE_ADMIN"})
  public JsonResult writeOffSettlement(int enterpriseId, String username, int settlementId,
                                       int receiptId) {
    if (receiptId < 0) {
      return JsonResult.createJsonResult(-6, "收款单id错误");
    }

    SettlementSheet ss = settlementDao.getSettlementSheetById(settlementId);
    if (ss == null || ss.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-3, "不存在结算单");
    }
    if (ss.getPaid() == 1) {
      return JsonResult.createJsonResult(-4, "此结算单已核销");
    }

    int retCode;
    if (receiptId == 0) {
      // 对总金额为0的结算单进行销账
      if (ss.getAmount() != 0) {
        return JsonResult.createJsonResult(-7, "结算单金额不为0");
      }

      retCode = settlementDao.writeOffZeroSettlementAmount(enterpriseId, username, settlementId);
    } else {
      ReceiptInfo receiptInfo = this.refreshReceiptInfo(enterpriseId, receiptId);
      if (receiptInfo == null) {
        return JsonResult.createJsonResult(-1, "不存在此收款单");
      }
      if (receiptInfo.getStatus() == 1) {
        return JsonResult.createJsonResult(-2, "收款单已完全核销");
      }

      if (receiptInfo.getAmount() - receiptInfo.getWriteOffAmount() - ss.getAmount() < 0) {
        return JsonResult.createJsonResult(-5, ERROR_MESSAGE_AMOUNT_LESS);
      }

      retCode = settlementDao.writeOffSettlement(enterpriseId, username, settlementId, receiptId);
    }

    return JsonResult.createJsonResult(retCode);
  }


  @Override
  @Secured({"ROLE_ADMIN"})
  public JsonResult undoWriteOffSettlement(int enterpriseId, String username, int settlementId,
                                           String remark) {
    if (!"OK".equalsIgnoreCase(remark)) {
      return JsonResult.createJsonResult(-1, "请输入 OK，以确认撤销销账。");
    }

    int retCode = settlementDao.undoWriteOffSettlement(enterpriseId, username, settlementId);

    if (retCode > 0) {
      String remark1 = "撤销结算单(" + settlementId + ")销账:成功。";
      doSendAlert(enterpriseId, remark1, null, EntDepartment.DEPT_ADMINISTRATOR);
    }

    return JsonResult.createJsonResult(retCode);
  }


  @Override
  public List<ReceiptHistory> searchReceiptHistory(int enterpriseId, int id) {
    return billDao.searchReceiptHistory2(enterpriseId, id);
  }


  @Override
  @Secured({"ROLE_CASHIER", "ROLE_ADMIN"})
  public JsonResult deleteReceipt(int enterpriseId, String username, int id) {
    int retCode = billDao.deleteReceipt(enterpriseId, username, id);
    return JsonResult.createJsonResult(retCode);
  }


  @Override
  @Secured({"ROLE_CASHIER", "ROLE_ADMIN"})
  public JsonResult refundReceipt(int enterpriseId, String username, int id, double amount,
                                  String remark) {
    ReceiptInfo receiptInfo = billDao.refreshReceiptInfo(id);
    if (receiptInfo == null || receiptInfo.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-1, "收款单不存在");
    }

    if (amount > receiptInfo.getBalance()) {
      return JsonResult.createJsonResult(-2, "金额过大");
    }

    int retCode = billDao.refundReceipt(enterpriseId, username, id, amount, remark);
    return JsonResult.createJsonResult(retCode);
  }


  @Override
  @Secured({"ROLE_CASHIER", "ROLE_ADMIN"})
  public JsonResult updateReceiptRemark(int enterpriseId, String username, int id, String remark) {
    int retCode = billDao.updateReceiptRemark(enterpriseId, username, id, remark);
    return JsonResult.createJsonResult(retCode);
  }

  @Override
  @Secured({"ROLE_CASHIER", "ROLE_ADMIN"})
  public JsonResult downloadAlipayBillAccountLog(final int enterpriseId, final String username, final Date beginDate,
                                                 final Date endDate) {
    //下载支付宝账户账务明细，保存
    //若是新的内容，则创建同等金额的收款单，并关联
    var rq = new BillAccountLogRequest();
    rq.setStartTime(beginDate);
    rq.setEndTime(endDate);

    var alipayService = createAlipayService(enterpriseId);
    if (alipayService == null) {
      return JsonResult.createJsonResult(-1, "未找到开发参数");
    }

    //TODO 待解决数据量超过2000的翻页问题 yfdai 2021-10-27
    var rs = alipayService.queryBillAccountLog(rq);
    if (!rs.isSuccess()) {
      return JsonResult.createJsonResult(-2, rs.getMsg() + ": " + rs.getSubMsg());
    }

    int saved = 0;
    for(var item: rs.getDetailList()) {
      var log = new AlipayBillAccountLog();
      BeanUtils.copyProperties(item, log);
      log.setEnterpriseId(enterpriseId);
//      log.setMainAccount(); alipayService.getMainAccount

      double amount = Double.parseDouble(log.getTransAmount());
      if (amount < 0) {
        //支出记录暂不处理
        continue;
      }

      try {
        int alipayBillAccountLogId =  billDao.createAlipayBillAccountLog(enterpriseId, username, log);

        final String receiptNo = orderNoGenerator.generateOrderNo(enterpriseId, OrderNoInfo.ENT_RECEIPT);

        ReceiptInfo info = new ReceiptInfo();
        info.setEnterpriseId(enterpriseId);
        info.setOperator(username);

        info.setReceiptNo(receiptNo);
        info.setAmount(amount);

        if (amount >= 0) {
          info.setReceiptType(RECEIPT_TYPE_RECEIVE);
        } else if (amount < 0) {
          // 退款单
          info.setReceiptType(RECEIPT_TYPE_REFUND);
        }

        info.setRemark(log.getOtherAccount());

        info.setReceiptBank("支付宝");
        info.setReceiptDate(DateUtil.convertToDate(log.getTransDt(), "yyyy-MM-dd HH:mm:ss"));

        billDao.createReceiptInfoByAlipayBillAccountLog(enterpriseId, username, info, alipayBillAccountLogId);

        saved++;
      } catch (Exception ex) {
        logger.error(ex.getMessage());
      }
    }

    return JsonResult.createJsonResult(1, "下载了" + saved + "张账单");
  }

  @Override
  public JsonResult updateReceiptCustomer(final int enterpriseId, final String username, final int receiptId, final int customerId) {
    var receiptInfo = billDao.refreshReceiptInfo(receiptId);
    if (receiptInfo == null || receiptInfo.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-1, "收款单不存在");
    }

    if (receiptInfo.getCustomer() != null) {
      return JsonResult.createJsonResult(-1, "收款单已指定客户");
    }

    var customerInfo = customerDao.getCustomerById(customerId);
    if (customerInfo == null || customerInfo.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-2, "所指定的客户不存在");
    }

    int retCode = billDao.updateReceiptCustomer(enterpriseId, username, receiptId, customerId);
    return JsonResult.createJsonResult(retCode);
  }

  @Override
  public JsonResult mergeReceipts(AuthInfo authInfo, Integer[] receiptIds) {
    try {
      if (receiptIds.length < 2) {
        return JsonResult.createJsonResult(-1, "需要至少2张收款单才能合并");
      }
      int retCode = billDao.mergeReceipts(authInfo, receiptIds);
      return JsonResult.createJsonResult(retCode);
    } catch (Exception ex) {
      return JsonResult.createJsonResult(-100, ex.getMessage());
    }
  }


  /**
   * 校验收款单用于销账
   */
  private JsonResult validateReceiptInfoForWriteOff(ReceiptInfo receiptInfo) {
    if (receiptInfo == null) {
      return JsonResult.createJsonResult(-1, "收款单不存在");
    }
    if (receiptInfo.getStatus() == 1) {
      return JsonResult.createJsonResult(-2, "收款单已完全核销");
    }

    return null;
  }


  /**
   * 校验账单内容是否适于销账
   */
  private JsonResult validateBillInfo4WriteOff(final int enterpriseId, BillInfo billInfo) {
    if (billInfo == null || billInfo.getEnterpriseId() != enterpriseId) {
      return JsonResult.createJsonResult(-3, ERROR_MESSAGE_BILL_NOT_EXISTED);
    }

    if (BillInfo.CUSTOMER_PAYMENT_RECEIVED.equalsIgnoreCase(billInfo.getBePaid())) {
      return JsonResult.createJsonResult(-4, "账单(" + billInfo.getBillNum() + ")已核销");
    }

    if ("0".equalsIgnoreCase(billInfo.getAudited())) {
      return JsonResult.createJsonResult(-5, "账单(" + billInfo.getBillNum() + ")未审核");
    }

    if (!StringUtils.isEmpty(billInfo.getSettlementNo())) {
      return JsonResult
              .createJsonResult(-10, "账单(" + billInfo.getBillNum() + ")包含于结算单(" + billInfo.getSettlementNo() + "), 请通过结算单销账");
    }

    return null;
  }


  private JsonResult validateBillAndReceipt(BillInfo billInfo, ReceiptInfo receiptInfo, final boolean ignoreNegativeNum) {
    //收款单或退款单是否属于同一个月结客户或散客
    if (receiptInfo.getCustomerType() != billInfo.getCustomerType()) {
      return JsonResult.createJsonResult(-5, "所属公司类型不匹配");
    }

    if (receiptInfo.getCustomer() != null && receiptInfo.getCustomer().getId() != billInfo
            .getCustomerId()) {
      return JsonResult.createJsonResult(-5, "所属公司不匹配");
    }

    if (receiptInfo.getReceiptType() == RECEIPT_TYPE_REFUND) {
      // 退款单
      if (billInfo.getBillSum() > 0) {
        return JsonResult.createJsonResult(-6, "只能核销金额小于等于0的账单(" + billInfo.getBillNum() + ")");
      }

      if (receiptInfo.getBalance() > billInfo.getBillSum()) {
        return JsonResult.createJsonResult(-7, ERROR_MESSAGE_AMOUNT_LESS);
      }
    }

    if (receiptInfo.getReceiptType() == RECEIPT_TYPE_RECEIVE && !ignoreNegativeNum) {
      // 收款单
      if (billInfo.getBillSum() < 0) {
        return JsonResult.createJsonResult(-8, "只能核销金额大于等于0的账单(" + billInfo.getBillNum() + ")");
      }
      if (receiptInfo.getBalance() - billInfo.getBillSum() < 0) {
        return JsonResult.createJsonResult(-9, ERROR_MESSAGE_AMOUNT_LESS);
      }
    }

    return null;
  }

}
